﻿using System.Collections.Generic;
using Ewk.MachineLearning.Genetic.Programming.Engine;
using Ewk.MachineLearning.Genetic.Programming.Program;

namespace Ewk.MachineLearning.Genetic.Programming
{
    internal class PopulationFactory
    {
        private readonly EngineConfiguration _engineConfiguration;
        private readonly ProblemDefinition _problemDefinition;

        private readonly TerminalNodeFactory _terminalNodeFactory;
        private readonly FunctionNodeFactory _functionNodeFactory;

        #region Constructors

        /// <summary>
        /// Initializes a new instance.
        /// </summary>
        /// <param name="engineConfiguration">Configuration of the genetic programming engine.</param>
        /// <param name="problemDefinition">An object with properties that define the problem to be solved.</param>
        public PopulationFactory(EngineConfiguration engineConfiguration, ProblemDefinition problemDefinition)
        {
            _engineConfiguration = engineConfiguration;
            _problemDefinition = problemDefinition;

            _terminalNodeFactory = problemDefinition.Constants == null
                           ? new TerminalNodeFactory(problemDefinition.Variables)
                           : new TerminalNodeFactory(problemDefinition.Variables, problemDefinition.Constants);
            _functionNodeFactory = problemDefinition.Functions == null
                                       ? new FunctionNodeFactory()
                                       : new FunctionNodeFactory(problemDefinition.Functions);
        }

        #endregion

        public IEnumerable<VariableNode> VariableNodes
        {
            get { return _terminalNodeFactory.VariableNodes; }
        }

        public IEnumerable<Node> CreateInitialPopulation()
        {
            var nodeTreeFactory = new NodeTreeFactory(_engineConfiguration.MaxDepth, _terminalNodeFactory, _functionNodeFactory);
            var individuals = new List<Node>();

            for (var i = 0; i < _engineConfiguration.PopulationSize / 2; i++)
            {
                individuals.Add(nodeTreeFactory.Create(NodeTreeFactory.GenerationMethod.Grow, _problemDefinition.OutputType));
            }

            for (var i = _engineConfiguration.PopulationSize / 2; i < _engineConfiguration.PopulationSize; i++)
            {
                individuals.Add(nodeTreeFactory.Create(NodeTreeFactory.GenerationMethod.Full, _problemDefinition.OutputType));
            }

            return individuals;
        }
    }
}